#pragma once
#include "fstream.h"
#include "json.hpp"
#include "task.h"
#include "uri.h"
#include <cctype>
#include <cstdio>
#include <functional>
#include <netinet/in.h>
#include <string>
#include <unordered_map>
#include <utility>

using njson = nlohmann::json;

namespace co_async {

struct HttpPromise : Promise<void> {
  auto initial_suspend() { return std::suspend_always(); }
  auto final_suspend() noexcept { return PreviousAwaiter(m_previous); }
  auto get_return_object() {
    return std::coroutine_handle<HttpPromise>::from_promise(*this);
  }
  void unhandled_exception() {
    m_exception = std::current_exception();
    std::rethrow_exception(m_exception);
  }

  UnInit<FileStream> m_conn;
};

class HttpResponse {
public:
  friend class WebSocket;
  HttpResponse(int status = 200,
               std::unordered_map<std::string, std::string> &&header = {},
               std::string &&body = "");

  HttpResponse(HttpResponse &&that) = default;
  Task<> write_into(FileStream &sock);
  bool is_ws() const;

private:
  int m_status;
  std::unordered_map<std::string, std::string> m_headers;
  std::string m_body;
};

struct Method {
  enum MethodType {
    GET,
    POST,
    UNKNOWN,
  };
  static MethodType GetMethod(const std::string &method) {
    auto it = method_map.find(method);
    if (it != method_map.end()) {
      return it->second;
    }
    return UNKNOWN;
  }

private:
  MethodType m_method;
  static inline std::unordered_map<std::string, MethodType> method_map = {
      {"get", GET}, {"post", POST}};
};

class HttpRequest {
public:
  HttpRequest();
  HttpRequest(auto uri);
  Task<bool> read_from(FileStream &sock);

  const std::string &get_path() const;
  const std::unordered_map<std::string, std::string> &get_queries() const;
  Method::MethodType get_method();
  const std::unordered_map<std::string, std::string> &get_header() const;

  const njson &get_params();
  const std::string &get_body() const;

private:
  Method::MethodType m_method;
  std::string m_body;
  std::unordered_map<std::string, std::string> m_header;
  Uri m_uri;
  njson m_params;
  static std::unordered_map<std::string,
                            std::function<njson(const std::string &)>>
      Http_Content_Handler;

  Task<bool> parse_url(const std::string &line);
  Task<bool> parse_header(const std::string &line);
};

} // namespace co_async